/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.datatheorem.ohos.trustkit.reporting;


import com.datatheorem.ohos.trustkit.asyntaskutils.Distribution;
import com.datatheorem.ohos.trustkit.asyntaskutils.TaskDispatcherCallBack;
import com.datatheorem.ohos.trustkit.ohad.Base64;
import com.datatheorem.ohos.trustkit.pinning.SystemTrustManager;
import com.datatheorem.ohos.trustkit.utils.TrustKitLog;
import ohos.app.Context;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;


// This returns an obscure threading error on API level < 16
class BackgroundReporterTask extends Distribution<ArrayList<Object>> {

    private static final SSLSocketFactory systemSocketFactory = getSystemSSLSocketFactory();

    public BackgroundReporterTask(Context context, TaskDispatcherCallBack callBack) {
        super(context, callBack);
    }

    @Override
    public void doSomething(ArrayList<Object> params) {
        Integer lastResponseCode = null;

        // First parameter is the report
        PinningFailureReport report = (PinningFailureReport) params.get(0);

        // Remaining parameters are report URLs - send the report to each of them
        for (int i=1; i<params.size(); i++) {
            URL reportUri = (URL) params.get(i);
            HttpURLConnection connection = null;
            try {
                connection = (HttpURLConnection) reportUri.openConnection();
                connection.setRequestMethod("POST");
                connection.setRequestProperty("Content-Type", "application/json");
                connection.setDoOutput(true);
                connection.setChunkedStreamingMode(0);

                // If basic authentication was specified in the URL, set it up on the connection
                if (reportUri.getUserInfo() != null) {
                    String basicAuth = "Basic " + new String(Base64.encode(reportUri.getUserInfo().getBytes(), Base64.DEFAULT));
                    connection.setRequestProperty("Authorization", basicAuth);
                }

                if (connection instanceof HttpsURLConnection) {
                    // HTTPS URL
                    HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
                    // Use the default system factory - this will avoid an infinite loop of report
                    // uploads if the reporting server triggers SSL failures
                    httpsConnection.setSSLSocketFactory(systemSocketFactory);
                }

                connection.connect();

                final OutputStream stream = new BufferedOutputStream(connection.getOutputStream());
                stream.write(report.toJson().toString().getBytes("UTF-8"));
                stream.flush();
                stream.close();

                lastResponseCode = connection.getResponseCode();
            } catch (IOException e) {
                TrustKitLog.i("Background upload - task completed with error:" + e.getMessage());
            } finally {
                if (connection != null) {
                    connection.disconnect();
                }
            }
        }
//        return lastResponseCode;
    }

    private static SSLSocketFactory getSystemSSLSocketFactory() {
        SSLContext context;
        try {
            context = SSLContext.getInstance("TLS");
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("Should never happen");
        }
        if (context == null) {
            throw new IllegalStateException("Should never happen");
        }

        try {
            context.init(null, new TrustManager[] { SystemTrustManager.getInstance() }, null);
        } catch (KeyManagementException e) {
            throw new IllegalStateException("Should never happen");
        }
        return context.getSocketFactory();
    }
}

